import { Dictionary } from "../containers/Dictionary";
import { Entity } from "./Entity";
import { IComponent } from "./IComponent";
import { BaseMatcher, Matcher, MatcherAllOf, MatcherAnyOf } from "./Matcher";
import { Storage } from "./Storage";
import { System } from "./System";



/**
 * 世界
 */
export class World {

    static HELP_SYSTEM_LIST: Array<new () => System> = [];
    static HELP_ENTITY_LIST: Array<Entity> = [];
    
    /**最大实体数量 */
    private __max_count: number = 0;
    /**稀疏集合 */
    private __storage: Storage<Entity, IComponent>;
    /**系统列表 */
    private __systems: Dictionary<new () => System, System>;
    /**等待删除 */
    private __wait_free: Set<Entity>;

    constructor(max_count: number) {
        this.__max_count = max_count;
        this.__storage = new Storage<Entity, IComponent>(max_count);
        this.__systems = new Dictionary<new () => System, System>();
    }

    tick(dt: number) {
        const systems = this.__systems.elements;
        systems.forEach(s => s.tick(dt));
        this.__wait_free.forEach(e => this.__storage.remove(e));
        this.__wait_free.clear();
    }

    /**
     * 创建实体
     * @param entity 
     */
    createEntity(entity: Entity): void {
        this.__storage.add(entity);
    }

    /**
     * 查询是否包含实体
     * @param entity 
     * @returns 
     */
    hasEntity(entity: Entity): boolean {
        return this.__storage.has(entity);
    }

    /**
     * 删除实体
     * @param entity 
     */
    removeEntity(entity: Entity): void {
        if (!this.hasEntity(entity)) {
            throw new Error("Entity not found");
        }
        if (this.__wait_free.has(entity)) {
            return;
        }
        this.__wait_free.add(entity);
    }

    /**
     * 添加系统
     * @param type 
     */
    addSystem(type: new () => System): void {
        if (this.__systems.has(type)) {
            throw new Error("System already exists");
        }
        let sys = new type();
        sys.world = this;
        this.__systems.set(type, sys);
        //排序
        this.__systems.elements.sort((a, b) => a.priority - b.priority);
        //匹配
        this.__matcherAll(sys);
    }

    /**
     * 检测是否包含系统
     * @param type 
     * @returns 
     */
    hasSystem(type: new () => System): boolean {
        return this.__systems.has(type);
    }

    /**
     * 获取系统
     * @param type 
     * @returns 
     */
    getSystem(type: new () => System): System {
        return this.__systems.get(type);
    }

    /**
     * 删除系统
     * @param type 
     */
    removeSystem(type: new () => System): void {
        if (!this.__systems.has(type)) {
            throw new Error("System not found");
        }
        let sys = this.__systems.get(type);
        sys.destroy();
        this.__systems.delete(type);
    }

    /**
     * 添加组件
     * @param entity 
     * @param type 
     * @returns 
     */
    addComponent<T extends IComponent>(entity: Entity, type: new () => T): T {
        let result = this.__storage.addValue(entity, type);
        result.dirtySignal = () => {
            this.__component_dirty(entity);
        }
        //检测组件依赖的系统，如果没有则添加
        let list = World.HELP_SYSTEM_LIST;
        list.splice(0, list.length);
        result.getDependencies(list);
        if (list.length > 0) {
            for (let index = 0; index < list.length; index++) {
                const sys_class = list[index];
                if (!this.__systems.has(sys_class)) {
                    this.addSystem(sys_class);
                }
            }
        }
        //记录所属
        result.entity = entity;
        result.world = this;
        //匹配
        this.__matcher(entity, false, true);
        return result as T;
    }

    /**
     * 判断是否包含组件
     * @param entity 
     * @param type 
     * @param checkType     是否检查类型(instanceof)
     * @returns 
     */
    hasComponent<T extends IComponent>(entity: Entity, type: new () => T, checkType: boolean = false): boolean {
        if (!checkType) {
            return this.__storage.hasValue(entity, type);
        }
        let result = false;
        let entitySet = this.__storage.getEntitySet(entity);
        entitySet.forEach(value => {
            let com = this.__storage.getValue(entity, value);
            if (com instanceof type) {
                result = true;
                return;
            }
        });
        return result;
    }

    /**
     * 获取组件
     * @param entity 
     * @param type 
     * @param checkType 
     * @returns 
     */
    getComponent<T extends IComponent>(entity: Entity, type: new () => T, checkType: boolean = false): T | null {
        if (!checkType) {
            return this.__storage.getValue(entity, type) as T;
        }
        let result: T | null = null;
        if (!checkType) {
            let entitySet = this.__storage.getEntitySet(entity);
            entitySet.forEach(value => {
                const com = this.__storage.getValue(entity, value);
                if (com instanceof type) {
                    result = com as T;
                    return;
                }
            });
        }
        return result;
    }


    /**
     * 获取所有组件
     * @param type 
     * @param checkType 
     * @returns 
     */
    getComponents<T extends IComponent>(type: new () => T, checkType: boolean = false): Array<T> {
        if (!checkType) {
            return this.__storage.getValues(type) as Array<T>;
        }
        //entity num>component num so for components is fast
        let result: Array<T> = [];
        let types: Array<new () => IComponent> = [];
        let values = this.__storage.values;
        values.forEach((list, key) => {
            if (list.length > 0) {
                if (list.find(value => { return value instanceof type; })) {
                    types.push(key);
                }
            }
        });
        types.forEach(type => {
            result.push(...this.__storage.getValues(type) as Array<T>);
        });
        return result;
    }

    /**
     * 获取实体上的某个类型组件列表
     * @param entity 
     * @param type 
     * @returns 
     */
    getComponentList<T extends IComponent>(entity: Entity, type: new () => T): Array<T> {
        let result: Array<T> = [];
        let entitySet = this.__storage.getEntitySet(entity);
        entitySet.forEach(value => {
            const com = this.__storage.getValue(entity, value);
            if (com instanceof type) {
                result.push(com as T);
            }
        });
        return result;
    }

    /**
     * 获取组件，如果没有则添加
     * @param entity 
     * @param type 
     * @returns 
     */
    getAddComponent<T extends IComponent>(entity: Entity, type: new () => T): T {
        if (this.__storage.hasValue(entity, type)) {
            return this.getComponent(entity, type);
        }
        return this.addComponent(entity, type);
    }

    /**
     * 删除组件
     * @param entity 
     * @param type 
     */
    removeComponent(entity: Entity, type: new () => IComponent): void {
        this.__storage.removeValue(entity, type);
        this.__matcher(entity, false, true);
    }

    /**
     * 删除组件，如果存在则删除
     * @param entity 
     * @param type 
     */
    removeComponentIf(entity: Entity, type: new () => IComponent): void {
        if (this.hasComponent(entity, type)) {
            let result = this.__storage.removeValue(entity, type);
            this.__matcher(entity, false, true);
        }
    }

    /**
     * 删除组件
     * @param entity 
     * @param com 
     */
    removeComponentBy(entity: Entity, com: IComponent): void {
        this.__storage.removeValue(entity, com["constructor"] as new () => IComponent);
        this.__matcher(entity, false, true);
    }


    private __component_dirty(entity: Entity): void {
        this.__matcher(entity, true);
    }
    //========================匹配逻辑========================
    /**
     * 系统对所有entity进行匹配
     * @param sys 
     */
    private __matcherAll(sys: System): void {
        let list = World.HELP_ENTITY_LIST;
        list.splice(0, list.length);
        this.__storage.getKeys(list);
        list.forEach((entity) => {
            this.__matcherEntity(sys.matcher, entity);
        });
    }

    /**
     * entity与所有系统进行匹配
     * @param entity 
     * @param dirty         dirty标记
     * @param all           是否忽略dirty标记
     */
    private __matcher(entity: Entity, dirty = false, all = false): void {
        let systems = this.__systems.elements;
        for (let index = 0; index < systems.length; index++) {
            const sys = systems[index];
            if (sys.useDirty == dirty || all) {
                if (this.__matcherEntity(sys.matcher, entity)) {
                    if (!sys.matcher.entitys.has(entity)) {
                        sys.matcher.entitys.add(entity);
                    }
                } else {
                    if (sys.matcher.entitys.has(entity)) {
                        sys.matcher.entitys.delete(entity);
                    }
                }
            }
        }
    }

    private __matcherEntity(matcher: Matcher, entity: Entity): boolean {
        let mainMatcher = this.__matcherComponents(matcher.matcher, entity);
        let anyMatcher = this.__matcherComponents(matcher.anyOf, entity);
        let notMatcher = this.__matcherComponents(matcher.noneOf, entity);
        return mainMatcher && anyMatcher && !notMatcher;
    }

    private __matcherComponents(matcher: BaseMatcher, entity: Entity): boolean {
        if (matcher instanceof MatcherAllOf) {
            for (let index = 0; index < matcher.types.length; index++) {
                const type = matcher.types[index];
                if (!this.hasComponent(entity, type)) {
                    return false;
                }
            }
        } else if (matcher instanceof MatcherAnyOf) {
            for (let index = 0; index < matcher.types.length; index++) {
                const type = matcher.types[index];
                if (this.hasComponent(entity, type)) {
                    return true;
                }
            }
            return false;
        } else {
            for (let index = 0; index < matcher.types.length; index++) {
                const type = matcher.types[index];
                if (this.hasComponent(entity, type)) {
                    return false;
                }
            }
        }
        return true;
    }
}